Vue3 define 宏函数详解

Ray Shine 2024/7/15 Vue3进阶知识definePropsdefineEmitsdefineExpose

在Vue3的<script setup>语法糖中,为了更好地支持组合式API和TypeScript,Vue引入了三个编译时宏函数:definePropsdefineEmitsdefineExpose。这些宏函数无需导入即可直接使用,它们在编译阶段被处理,用于定义组件的输入(props)、输出(emits)和公共接口(expose),从而实现更类型安全、更简洁的组件开发。

# 1. defineProps():定义组件属性

defineProps()宏函数用于声明组件接收的props。它返回一个props对象,可以直接在<script setup>中解构或作为普通对象使用。defineProps支持两种主要的声明方式:运行时类型声明和TypeScript类型声明。

# 运行时类型声明

这种方式与Vue2的props选项类似,通过一个对象来定义props的类型、默认值、是否必需等。

<script setup>
const props = defineProps({
  // 基础类型检查
  msg: String,
  // 带有默认值
  count: {
    type: Number,
    default: 0
  },
  // 必需的属性
  requiredProp: {
    type: String,
    required: true
  },
  // 自定义验证函数
  validatorProp: {
    type: String,
    validator: (value) => {
      return ['success', 'warning', 'danger'].includes(value);
    }
  }
});

console.log(props.msg); // 访问props
</script>

# TypeScript类型声明

当使用TypeScript时,defineProps可以接受一个类型参数,从而提供更强大的类型推断和编译时检查。这是推荐的方式,因为它提供了最佳的开发体验。

<script setup lang="ts">
interface Props {
  msg?: string; // 可选属性
  count: number;
  items: string[];
}

// 使用类型参数声明props
const props = defineProps<Props>();

console.log(props.msg); // 自动推断为 string | undefined
console.log(props.count); // 自动推断为 number

// 也可以为props提供默认值,需要使用 withDefaults 宏函数
// const propsWithDefaults = withDefaults(defineProps<Props>(), {
//   msg: 'default message',
//   count: 1
// });
</script>

# 2. defineEmits():定义组件事件

defineEmits()宏函数用于声明组件可以触发的自定义事件。它返回一个emit函数,用于在组件内部触发事件。同样,defineEmits也支持运行时声明和TypeScript类型声明。

# 运行时声明

通过一个字符串数组来声明事件名称。

<script setup>
const emit = defineEmits(['update', 'delete']);

const handleClick = () => {
  emit('update', 123); // 触发 'update' 事件,并传递数据
};
</script>

# TypeScript类型声明

通过一个类型字面量来声明事件,可以指定事件名称和传递的参数类型,提供更严格的类型检查。

<script setup lang="ts">
const emit = defineEmits<{
  (e: 'update', id: number, value: string): void;
  (e: 'delete', id: number): void;
  (e: 'change', event: Event): void;
}>();

const handleUpdate = () => {
  emit('update', 1, 'new value');
};

const handleDelete = (id: number) => {
  emit('delete', id);
};
</script>

# 3. defineExpose():暴露公共接口

默认情况下,当父组件通过模板引用(ref)访问子组件实例时,如果子组件使用了<script setup>,那么父组件将无法访问到子组件内部的任何变量或方法。defineExpose()宏函数就是为了解决这个问题,它允许你显式地暴露组件实例的公共属性和方法。

<!-- ChildComponent.vue -->
<script setup>
import { ref } from 'vue';

const privateData = ref('I am private');
const publicData = ref('I am public');

const privateMethod = () => {
  console.log('Private method called');
};
const publicMethod = () => {
  console.log('Public method called');
};

// 只有 publicData 和 publicMethod 会被暴露给父组件
defineExpose({
  publicData,
  publicMethod
});
</script>
<!-- ParentComponent.vue -->
<template>
  <ChildComponent ref="childRef" />
  <button @click="callChildMethod">Call Child Method</button>
</template>

<script setup>
import { ref, onMounted } from 'vue';
import ChildComponent from './ChildComponent.vue';

const childRef = ref(null);

onMounted(() => {
  if (childRef.value) {
    console.log(childRef.value.publicData); // 访问暴露的属性
    childRef.value.publicMethod(); // 调用暴露的方法
    // console.log(childRef.value.privateData); // 无法访问,会报错
  }
});

const callChildMethod = () => {
  if (childRef.value) {
    childRef.value.publicMethod();
  }
};
</script>

# 总结

definePropsdefineEmitsdefineExpose是Vue3 <script setup>中构建组件接口的核心宏函数。它们不仅简化了代码,更重要的是,通过与TypeScript的结合,提供了强大的类型安全保障,使得组件的开发更加健壮和可维护。熟练掌握这些宏函数的使用,是高效开发Vue3组件的关键。

最后更新时间: 2025/11/20 22:59:30
ON THIS PAGE